#include <iostream>
#include <string>
<<<<<<< HEAD
#include <functional>
#include <unordered_map>
#include <cassert>
#include <sys/epoll.h>
#include "easysock.hpp"
#include "util.hpp"
=======
#include <sys/poll.h>
#include "easysock.hpp"
>>>>>>> 6a45de23e2d17a149b05ebb82348494752818695

namespace server
{

<<<<<<< HEAD
struct connect;

using callback_t = std::function<void(connect&)>;
using service_t  = std::function<void(connect&)>;

enum 
{
    in   = EPOLLIN,
    out  = EPOLLOUT,
    io   = EPOLLIN  | EPOLLOUT,
    err  = EPOLLERR | EPOLLHUP,
    et   = EPOLLET,
};

class buffer
{
public:
    buffer() : begin(0), end(0) {}

    void push(std::string s)
    {
        buf += s;
        end += s.size();
    }

    std::string pop(ssize_t len)
    {
        assert(begin + len <= end);

        std::string res = buf.substr(begin, len);
        begin += len;
        return res;
    }

    bool empty() { return begin == end; }

    std::string get() { return buf.substr(begin); }

private:
    std::string buf;
    ssize_t begin;
    ssize_t end;
};

struct epev
{
    epev(int f, int e = in, bool etm = false) : fd(f), ev(etm ? e|et : e) {}
    int fd;
    int ev;
};

struct epin  : public epev { epin (int f, bool etm = false) : epev(f, etm ? et|in  : in ) {} };
struct epout : public epev { epout(int f, bool etm = false) : epev(f, etm ? et|out : out) {} };
struct epio  : public epev { epio (int f, bool etm = false) : epev(f, etm ? et|io  : io ) {} };
struct eperr : public epev { eperr(int f, bool etm = false) : epev(f, etm ? et|err : err) {} };

struct connect
{
    connect() {}

    connect(int f, int e, bool etm = false, const std::string& i = "", uint16_t p = 0) : fd(f), ev(e), etmode(etm), ip(i), port(p)
    {}

    void register_cb(callback_t recv, callback_t send, callback_t except)
    {
        recver = recv;
        sender = send;
        excepter = except;
    }

    int fd;
    int ev;
    bool etmode;

    std::string ip;
    uint16_t port;

    buffer ibuf, obuf;

    callback_t recver;
    callback_t sender;
    callback_t excepter;
};

class epoll
{
public:
    epoll(int size = 20) : _epfd(0), _size(size), _evs(new struct epoll_event[size])
    {}

    void create()
    {
        _epfd = epoll_create(128);
        assert(_epfd > 0);
    }

    void add_event(const epev& ev) { control(EPOLL_CTL_ADD, ev); }
    void del_event(const epev& ev) { control(EPOLL_CTL_DEL, ev); }
    void mod_event(const epev& ev) { control(EPOLL_CTL_MOD, ev); }

    int wait(int timeout) { return epoll_wait(_epfd, _evs.get(), _size, timeout); }

    struct epoll_event& operator[](int i) { return _evs[i]; };

private:
    void control(int op, const epev& ev)
    {
        struct epoll_event epev;
        epev.data.fd = ev.fd;
        epev.events = ev.ev;
        assert(epoll_ctl(_epfd, op, ev.fd, &epev) == 0);
    }

    int _epfd;
    int _size;
    std::unique_ptr<struct epoll_event[]> _evs;
};

class epoll_server : public inet::tcp::server
{
public:
    epoll_server(int port, int timeout) : server(port), _timeout(timeout), _etmode(false)
=======
class epoll_server : public inet::tcp::server
{
public:
    epoll_server(int port) : server(port)
>>>>>>> 6a45de23e2d17a149b05ebb82348494752818695
    {}

    ~epoll_server() 
    {}

<<<<<<< HEAD
    void init()
    {
        _ep.create();
        add_conn(_sock, in);
    }

    void looper()
    {
        while (true)
        {
            loop_once();
        }
    }
    
    void loop_once()
    {
        int n = _ep.wait(_timeout);
        if (n > 0)
        {
            for (int i = 0; i < n; i++)
            {
                const auto& fd = _ep[i].data.fd;
                const auto& ev = _ep[i].events;

                if (ev & in)  _conns[fd].recver(_conns[fd]);
                if (ev & out) _conns[fd].sender(_conns[fd]);
                if (ev & err) _conns[fd].excepter(_conns[fd]);
            }
=======
    void start()
    {
        while (true)
        {
>>>>>>> 6a45de23e2d17a149b05ebb82348494752818695
        }
    }

private:
<<<<<<< HEAD
    void acceptor(connect& conn)
    {
        do 
        {
            std::string ip;
            uint16_t port;
            int sock = accept(&ip, &port);

            if (sock < 0)
                break;  

            add_conn(sock, in, ip, port);
        } 
        while (conn.etmode);
    }

    void recver(connect& conn)
    {
        do
        {
            std::string buf;
            ssize_t s = recv(conn.fd, &buf, 1024);

            if (s > 0)
            {
                conn.ibuf.push(buf);
                send(conn.fd, "server return >>> " + buf);
            }
            else if (s == 0)
            {
                conn.excepter(conn);
                break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    conn.excepter(conn);
                    break;
                }
            }
        }
        while (conn.etmode);
    }

    void sender(connect& conn)
    {
        int fd = conn.fd;

        do
        {
            ssize_t s = send(conn.fd, conn.obuf.get());

            if (s > 0)
            {
                conn.obuf.pop(s);
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    conn.excepter(conn);
                    break;
                }
            }
        }
        while (conn.etmode);

        if (is_exist(fd) && conn.obuf.empty()) _ep.mod_event(epin(fd));
    }

    void excepter(connect& conn)
    {
        del_conn(conn.fd);
    }

private:

    void add_conn(int fd, int ev, const std::string& ip = "", uint16_t port = 0)
    {
        if (_etmode)
            util::set_nonblock(fd);

        connect conn(fd, ev, _etmode, ip, port);

        if (fd == _sock)
        {
            conn.register_cb(std::bind(&epoll_server::acceptor, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        {
            conn.register_cb(std::bind(&epoll_server::recver,   this, std::placeholders::_1), 
                             std::bind(&epoll_server::sender,   this, std::placeholders::_1), 
                             std::bind(&epoll_server::excepter, this, std::placeholders::_1));
        }

        _conns.insert({fd, std::move(conn)});
        _ep.add_event(epev(fd, ev, _etmode));
    }

    void del_conn(int fd)
    {
        _ep.del_event(epev(fd));
        _conns.erase(fd);
        close(fd);
    }

    bool is_exist(int fd) { return _conns.find(fd) != _conns.end(); }

private:
    epoll _ep;
    int _timeout;
    bool _etmode;
    std::unordered_map<int, connect> _conns;
=======
    std::unique_ptr<struct pollfd[]> _polls;
    int _size;
>>>>>>> 6a45de23e2d17a149b05ebb82348494752818695
};

}

